import {
  Controller,
  Post,
  Get,
  Body,
  UseGuards,
  HttpCode,
  HttpStatus,
  Req,
  Res,
  UnauthorizedException,
  BadRequestException,
  Query
} from '@nestjs/common'
import type { Request, Response } from 'express'
import { ApiTags, ApiOperation, ApiResponse } from '@nestjs/swagger'
import { AuthService } from '../../auth/auth.service'
import {
  LoginDto,
  RegisterDto,
  RefreshTokenDto,
  ForgotPasswordDto,
  ResetPasswordDto,
  VerifyEmailDto,
  ResendVerificationDto,
  LoginResponseDto,
  RegisterResponseDto
} from '../../common/dto/auth.dto'
import { UserStatus } from '../../common/dto/user.dto'
import { JwtAuthGuard } from '../../common/guards/auth.guard'
import { Public } from '../../common/decorators/auth.decorator'
import { CurrentUser } from '../../common/decorators/user.decorator'
import type { UserInfo } from '../../common/decorators/user.decorator'
import { ApiDoc } from '../../common/decorators/api.decorator'
import { ResponseMessage, SkipResponseTransform } from '../../common/decorators/response.decorator'
import { LoggerService } from '../../common/logger/logger.service'
import { RateLimitGuard } from '../../common/guards/auth.guard'

@ApiTags('用户认证')
@Controller('auth')
export class AuthController {
  constructor(
    private readonly authService: AuthService,
    private readonly logger: LoggerService
  ) {}

  @Post('register')
  @Public()
  @UseGuards(RateLimitGuard)
  @ResponseMessage('注册成功')
  async register(@Body() registerDto: RegisterDto): Promise<RegisterResponseDto> {
    this.logger.info('用户注册请求', {
      username: registerDto.username,
      email: registerDto.email
    })

    const result = await this.authService.register(registerDto)

    this.logger.info('用户注册成功', {
      userId: result.id,
      username: result.username,
      email: result.email
    })

    return {
      userId: result.id,
      username: result.username,
      email: result.email,
      status: UserStatus.ACTIVE,
      requiresEmailVerification: false,
      verificationEmailSent: false,
      registeredAt: new Date()
    }
  }

  @Post('login')
  @Public()
  @UseGuards(RateLimitGuard)
  @HttpCode(HttpStatus.OK)
  @ResponseMessage('登录成功')
  async login(@Body() loginDto: LoginDto, @Res({ passthrough: true }) response: Response): Promise<LoginResponseDto> {
    this.logger.info('用户登录请求', {
      identifier: loginDto.identifier
    })

    const result = await this.authService.login(loginDto)

    // 设置HttpOnly Cookie（可选）
    if (process.env.USE_HTTP_ONLY_COOKIES === 'true') {
      response.cookie('refreshToken', result.refreshToken, {
        httpOnly: true,
        secure: process.env.NODE_ENV === 'production',
        sameSite: 'strict',
        maxAge: 7 * 24 * 60 * 60 * 1000 // 7天
      })
    }

    this.logger.info('用户登录成功', {
      userId: result.user.id,
      username: result.user.username
    })

    return {
      accessToken: result.accessToken,
      refreshToken: result.refreshToken,
      tokenType: 'Bearer',
      expiresIn: result.expiresIn,
      refreshExpiresIn: 30 * 24 * 60 * 60, // 30天
      user: {
        id: result.user.id,
        username: result.user.username,
        email: result.user.email,
        nickname: result.user.nickname,
        avatar: result.user.avatar,
        role: result.user.role,
        status: UserStatus.ACTIVE,
        emailVerified: result.user.emailVerified || false,
        twoFactorEnabled: result.user.twoFactorEnabled || false,
        lastLoginAt: new Date()
      }
    }
  }

  @Post('logout')
  @UseGuards(JwtAuthGuard)
  @HttpCode(HttpStatus.NO_CONTENT)
  @ApiDoc({
    summary: '用户登出',
    description: '注销当前用户会话'
  })
  @ResponseMessage('登出成功')
  async logout(
    @CurrentUser() user: UserInfo,
    @Body() body: { refreshToken?: string },
    @Req() request: Request,
    @Res({ passthrough: true }) response: Response
  ): Promise<void> {
    const refreshToken = body.refreshToken || request.cookies?.refreshToken

    this.logger.info('用户登出请求', {
      userId: user.id,
      username: user.username,
      ip: request.ip
    })

    if (refreshToken) {
      await this.authService.logout(refreshToken)
    }

    // 清除Cookie
    response.clearCookie('refreshToken')

    this.logger.info('用户登出成功', {
      userId: user.id,
      username: user.username
    })
  }

  @Post('refresh')
  @Public()
  @HttpCode(HttpStatus.OK)
  @ApiDoc({
    summary: '刷新访问令牌',
    description: '使用刷新令牌获取新的访问令牌'
  })
  @ResponseMessage('令牌刷新成功')
  async refresh(
    @Body() refreshTokenDto: RefreshTokenDto,
    @Req() request: Request
  ): Promise<{ accessToken: string; refreshToken: string }> {
    const refreshToken = refreshTokenDto.refreshToken || request.cookies?.refreshToken

    if (!refreshToken) {
      this.logger.warn('刷新令牌缺失')
      throw new UnauthorizedException('刷新令牌缺失')
    }

    this.logger.debug('刷新令牌请求', {
      ip: request.ip
    })

    const result = await this.authService.refreshToken(refreshToken)

    this.logger.info('令牌刷新成功', {
      ip: request.ip
    })

    return {
      accessToken: result.accessToken,
      refreshToken: refreshToken // 使用原来的refreshToken
    }
  }

  @Post('forgot-password')
  @Public()
  @UseGuards(RateLimitGuard)
  @HttpCode(HttpStatus.OK)
  @ApiDoc({
    summary: '忘记密码',
    description: '发送密码重置邮件'
  })
  @ResponseMessage('密码重置邮件已发送')
  async forgotPassword(@Body() forgotPasswordDto: ForgotPasswordDto): Promise<{ message: string }> {
    this.logger.info('忘记密码请求', {
      email: forgotPasswordDto.email
    })

    await this.authService.forgotPassword(forgotPasswordDto.email)

    this.logger.info('忘记密码成功', {
      email: forgotPasswordDto.email
    })

    return {
      message: '如果该邮箱存在，您将收到密码重置邮件'
    }
  }

  @Post('reset-password')
  @Public()
  @UseGuards(RateLimitGuard)
  @HttpCode(HttpStatus.OK)
  @ApiDoc({
    summary: '重置密码',
    description: '使用重置令牌设置新密码'
  })
  @ResponseMessage('密码重置成功')
  async resetPassword(@Body() resetPasswordDto: ResetPasswordDto): Promise<{ message: string }> {
    this.logger.info('重置密码请求', {
      token: resetPasswordDto.token.substring(0, 10) + '...'
    })

    await this.authService.resetPassword(resetPasswordDto.token, resetPasswordDto.newPassword, {})

    this.logger.info('密码重置成功')

    return {
      message: '密码重置成功，请使用新密码登录'
    }
  }

  @Post('verify-email')
  @Public()
  @HttpCode(HttpStatus.OK)
  @ApiDoc({ summary: '验证邮箱' })
  @ResponseMessage('邮箱验证成功')
  async verifyEmail(@Body() verifyEmailDto: VerifyEmailDto): Promise<{ message: string; user: any }> {
    this.logger.info('邮箱验证请求', {
      token: verifyEmailDto.token.substring(0, 10) + '...'
    })

    const user = await this.authService.verifyEmail(verifyEmailDto.token, {})

    this.logger.info('邮箱验证成功', {
      userId: user.id,
      email: user.email
    })

    return {
      message: '邮箱验证成功',
      user
    }
  }

  @Post('resend-verification')
  @Public()
  @UseGuards(RateLimitGuard)
  @HttpCode(HttpStatus.OK)
  @ApiDoc({
    summary: '重发验证邮件',
    description: '重新发送邮箱验证邮件'
  })
  @ResponseMessage('验证邮件已重新发送')
  async resendVerification(@Body() resendDto: ResendVerificationDto): Promise<{ message: string }> {
    this.logger.info('重发验证邮件请求', {
      email: resendDto.email
    })

    await this.authService.resendVerificationEmail(resendDto.email, {})

    this.logger.info('验证邮件重发成功', {
      email: resendDto.email
    })

    return {
      message: '验证邮件已重新发送'
    }
  }

  @Get('me')
  @UseGuards(JwtAuthGuard)
  @ApiDoc({
    summary: '获取当前用户信息',
    description: '获取当前登录用户的详细信息'
  })
  async getProfile(@CurrentUser() user: UserInfo): Promise<UserInfo> {
    this.logger.debug('获取用户信息请求', {
      userId: user.id
    })

    return this.authService.getProfile(user.id)
  }

  @Post('validate-token')
  @Public()
  @HttpCode(HttpStatus.OK)
  @ApiDoc({
    summary: '验证访问令牌',
    description: '验证JWT访问令牌的有效性'
  })
  async validateToken(@Body('token') token: string): Promise<{ valid: boolean; user?: any; error?: string }> {
    if (!token) {
      throw new BadRequestException('令牌不能为空')
    }

    this.logger.debug('验证令牌请求', {
      token: token.substring(0, 20) + '...'
    })

    try {
      const user = await this.authService.validateToken(token)

      this.logger.debug('令牌验证成功', {
        userId: user.id
      })

      return {
        valid: true,
        user
      }
    } catch (error) {
      this.logger.warn('令牌验证失败')

      return {
        valid: false,
        error: error.message
      }
    }
  }

  @Get('sessions')
  @UseGuards(JwtAuthGuard)
  @ApiDoc({
    summary: '获取用户会话',
    description: '获取当前用户的所有活跃会话'
  })
  async getSessions(@CurrentUser() user: UserInfo) {
    this.logger.debug('获取用户会话请求', {
      userId: user.id
    })

    return this.authService.getUserSessions(user.id)
  }

  @Post('sessions/:sessionId/revoke')
  @UseGuards(JwtAuthGuard)
  @HttpCode(HttpStatus.NO_CONTENT)
  @ApiDoc({
    summary: '撤销会话',
    description: '撤销指定的用户会话'
  })
  @ResponseMessage('会话已撤销')
  async revokeSession(@CurrentUser() user: UserInfo, @Body('sessionId') sessionId: string): Promise<void> {
    this.logger.info('撤销会话请求', {
      userId: user.id,
      sessionId
    })

    await this.authService.revokeSession(user.id, sessionId)

    this.logger.info('会话撤销成功', {
      userId: user.id,
      sessionId
    })
  }

  @Post('sessions/revoke-all')
  @UseGuards(JwtAuthGuard)
  @HttpCode(HttpStatus.NO_CONTENT)
  @ApiDoc({
    summary: '撤销所有会话',
    description: '撤销当前用户的所有会话（除当前会话外）'
  })
  @ResponseMessage('所有会话已撤销')
  async revokeAllSessions(@CurrentUser() user: UserInfo, @Req() request: Request): Promise<void> {
    const currentToken = this.extractTokenFromHeader(request)

    this.logger.info('撤销所有会话请求', {
      userId: user.id
    })

    await this.authService.revokeAllSessions(user.id, currentToken)

    this.logger.info('所有会话撤销成功', {
      userId: user.id
    })
  }

  @Get('security-log')
  @UseGuards(JwtAuthGuard)
  @ApiDoc({
    summary: '获取安全日志',
    description: '获取当前用户的安全相关操作日志'
  })
  async getSecurityLog(
    @CurrentUser() user: UserInfo,
    @Query('page') page: number = 1,
    @Query('limit') limit: number = 20
  ): Promise<{
    logs: Array<{
      id: string
      action: string
      ip: string
      userAgent: string
      timestamp: Date
      success: boolean
      details?: any
    }>
    total: number
    page: number
    limit: number
  }> {
    this.logger.debug('获取安全日志请求', {
      userId: user.id,
      page,
      limit
    })

    return this.authService.getSecurityLog(user.id, { page, limit })
  }

  @Post('change-password')
  @UseGuards(JwtAuthGuard)
  @HttpCode(HttpStatus.NO_CONTENT)
  @ApiDoc({
    summary: '修改密码',
    description: '修改当前用户的登录密码'
  })
  @ResponseMessage('密码修改成功')
  async changePassword(
    @CurrentUser() user: UserInfo,
    @Body() body: { oldPassword: string; newPassword: string }
  ): Promise<void> {
    this.logger.info('修改密码请求', {
      userId: user.id
    })

    await this.authService.changePassword(
      user.id,
      body.oldPassword,
      body.newPassword,
      body.newPassword
    )

    this.logger.info('邮箱验证成功', {
      userId: user.id
    })
  }

  @Post('enable-2fa')
  @UseGuards(JwtAuthGuard)
  @ApiDoc({
    summary: '启用双因素认证',
    description: '为当前用户启用双因素认证'
  })
  @ResponseMessage('双因素认证已启用')
  async enableTwoFactor(
    @CurrentUser() user: UserInfo
  ): Promise<{ qrCode: string; secret: string; backupCodes: string[] }> {
    this.logger.info('启用双因素认证请求', {
      userId: user.id
    })

    const result = await this.authService.enableTwoFactor(user.id)

    this.logger.info('双因素认证启用成功', {
      userId: user.id
    })

    return {
      qrCode: result.qrCode,
      secret: result.secret,
      backupCodes: result.backupCodes
    }
  }

  @Post('disable-2fa')
  @UseGuards(JwtAuthGuard)
  @HttpCode(HttpStatus.NO_CONTENT)
  @ApiDoc({
    summary: '禁用双因素认证',
    description: '为当前用户禁用双因素认证'
  })
  @ResponseMessage('双因素认证已禁用')
  async disableTwoFactor(@CurrentUser() user: UserInfo, @Body('code') code: string): Promise<void> {
    this.logger.info('禁用双因素认证请求', {
      userId: user.id
    })

    await this.authService.disableTwoFactor(user.id, code)

    this.logger.info('双因素认证禁用成功', {
      userId: user.id
    })
  }

  @Post('verify-2fa')
  @Public()
  @HttpCode(HttpStatus.OK)
  @ApiDoc({
    summary: '验证双因素认证',
    description: '验证双因素认证代码'
  })
  @ResponseMessage('双因素认证验证成功')
  async verifyTwoFactor(@Body() body: { token: string; code: string }): Promise<LoginResponseDto> {
    this.logger.info('验证双因素认证请求')

    const result = await this.authService.verifyTwoFactor(body.token, body.code)

    this.logger.info('双因素认证验证成功', {
      userId: result.user.id
    })

    return result
  }

  private extractTokenFromHeader(request: Request): string | undefined {
    const [type, token] = request.headers.authorization?.split(' ') ?? []
    return type === 'Bearer' ? token : undefined
  }
}
